/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.filter.published;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.awt.Frame;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import shaman.storage.Factory;
import shaman.storage.FilterList;
import shaman.storage.api.Filter;
import shaman.storage.impl.filter.transposition.Permutation;
import shaman.storage.impl.stream.filter.block.BlockFilterInputStream;
import shaman.storage.impl.stream.filter.block.BlockFilterOutputStream;

/**
 *
 * @author dmitriy
 */
@XStreamAlias("deflate")
public class PermutateFilter implements Filter {

    public static final String FILTER_NAME="Перестановщик";
    public static final String PROPERTY_PERMUTATION_PERIOD="Период перестановки";
    public static final String PROPERTY_BLOCK_SIZE="Число периодов в блоке";

    private static class PermutFilterFactory implements Factory<PermutateFilter> {

        public String getTypeName() {
            return FILTER_NAME;
        }

        public PermutateFilter newObject() {
            return new PermutateFilter();
        }

        public Class<PermutateFilter> getGeneratedClass() {
            return PermutateFilter.class;
        }

    }


    private int blockSize=65536;
    private int permutationPeriod=2;
    private Permutation permutation;

    public PermutateFilter() {
    }

    public FilterType getFilterType() {
        return FilterType.PREPROCESSOR;
    }

    public String getName() {
        return FILTER_NAME;
    }

    public OutputStream getOutputStream(OutputStream compressedStream) throws IOException {
        return new BlockFilterOutputStream(compressedStream, getPermutation());
    }

    public InputStream getInputStream(InputStream compressedStream) throws IOException {
        return new BlockFilterInputStream(compressedStream, getPermutation());
    }

    public boolean hasConfigForm() {
        return false;
    }

    public boolean showConfigForm(Frame parent) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public List<String> getPropertyNames() {
        return Arrays.asList(new String[] {
            PROPERTY_BLOCK_SIZE,
            PROPERTY_PERMUTATION_PERIOD
        });
    }

    public String getPropertyValue(String name) throws InvalidParameterException {
        if (PROPERTY_BLOCK_SIZE.equals(name))
        {
            return String.valueOf(blockSize);
        }
        else if (PROPERTY_PERMUTATION_PERIOD.equals(name)) {
            return String.valueOf(permutationPeriod);
        }
        else {
            throw new InvalidParameterException("Неизвестное свойство: "+name);
        }
    }

    public List<String> getPropertyValues(String name) throws InvalidParameterException {
        if (PROPERTY_BLOCK_SIZE.equals(name) || PROPERTY_PERMUTATION_PERIOD.equals(name))
        {
            return null;
        }
        else {
            throw new InvalidParameterException("Неизвестное свойство: "+name);
        }
    }

    public void setPropertyValue(String name, String value) throws InvalidParameterException {
        if (PROPERTY_BLOCK_SIZE.equals(name) || PROPERTY_PERMUTATION_PERIOD.equals(name))
        {
            int newValue;
            try {
                newValue=Integer.parseInt(value);
            } catch (NumberFormatException ex) {
                throw new InvalidParameterException("Значение свойства должно быть числом");
            }
            if (newValue<=0)
                throw new InvalidParameterException("Значение свойства должно быть положительным");
            if (PROPERTY_BLOCK_SIZE.equals(name))
            {
                clearPermutation(newValue, 0);
            }
            else if (PROPERTY_PERMUTATION_PERIOD.equals(name))
            {
                clearPermutation(0, newValue);
            }
        }
        else {
            throw new InvalidParameterException("Неизвестное свойство: "+name);
        }
    }

    private synchronized void clearPermutation(int newBlockSize, int newPermutationPeriod)
    {
        if (newBlockSize>0 && blockSize!=newBlockSize || newPermutationPeriod>0 && permutationPeriod!=newPermutationPeriod)
        {
            permutation=null;
            if (newBlockSize>0)
                blockSize=newBlockSize;
            if (newPermutationPeriod>0)
                permutationPeriod=newPermutationPeriod;
        }
    }

    private synchronized Permutation getPermutation() {
        if (permutation!=null)
            return permutation;
        permutation=new Permutation(blockSize*permutationPeriod, build(blockSize, permutationPeriod));
        return permutation;
    }

    private static int[] build(int blockSize, int permutationPeriod)
    {
        int size=blockSize*permutationPeriod;
        int[] ret=new int[size];
        int i;
        for (i=0; i<size; i++)
        {
            ret[i]=(i%blockSize)*permutationPeriod+i/blockSize;
        }
        return ret;
    }

    static {
        FilterList.addFactory(new PermutFilterFactory());
    }
    
    public static void main(String[] args)
    {
        try {
            Filter permutFilter=FilterList.getFactoryMap().get(FILTER_NAME).newObject();
            ByteArrayOutputStream permuttedStream=new ByteArrayOutputStream();
            OutputStream permutStream=permutFilter.getOutputStream(permuttedStream);
            DataOutput permutData=new DataOutputStream(permutStream);
            permutData.writeUTF("У Мери был маленький ягнёнок");
            permutStream.close();
            permuttedStream.close();
            byte[] permuttedData=permuttedStream.toByteArray();
            InputStream unpermutStream=new ByteArrayInputStream(permuttedData);
            InputStream unpermuttedStream=permutFilter.getInputStream(unpermutStream);
            DataInput unpermutData=new DataInputStream(unpermuttedStream);
            String stringRead=unpermutData.readUTF();
            unpermuttedStream.close();
            unpermutStream.close();
            System.out.println(stringRead);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
